package com.xmh.myutil.util;

import lombok.extern.slf4j.Slf4j;

import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

@Slf4j
public class NioReadUtils {

    /**
     * @param path        绝对路径
     * @param charsetName UTF-8
     */
    public static final StringBuffer readFile(String path, String charsetName) {
        Charset c = Charset.forName(charsetName);
        // 解码器，字节流信息转换为字符信息
        CharsetDecoder decoder = c.newDecoder();
        try {
            RandomAccessFile raf = new RandomAccessFile(path, "r");

            FileChannel fc = raf.getChannel();

            // 设置成5是因为不管是UTF-8还是GBK在全中文文档都会出字节信息被截取的情况，也就是所谓会出现乱码。
            //这样方便测试

//            int capacity = 5;
            int capacity = 1024;

            StringBuffer stringBuffer = new StringBuffer();

            ByteBuffer bytes = ByteBuffer.allocate(capacity);
            CharBuffer cb = CharBuffer.allocate(capacity);
            int size = fc.read(bytes);
            while (size != -1) {
                bytes.flip();
                //size<5，小于5的时候说明文件已经读取结束了
                //为了防止源文件就是一个破损的文件，末尾有不符合规范的字节流
                //当末尾的时候就不做截留操作，直接转码
                decoder.decode(bytes, cb, size < capacity);
                cb.flip();
//                System.out.print(cb.toString());
                stringBuffer.append(cb.toString());
                //全中文文档第一次读取的时候，五个字节就是说UTF-8和GBK的文档都会有一个字节信息未被读取
                //这个时候需要保存多读取的信息，UTF-8中sub=2，后面两个属于非正常字节信息
                //GBK的时候sub=1 只有一个非正常的自己信息
                //后面继续读取的时候将会根据实际情况进行截留
                int sub = bytes.limit() - bytes.position();
                bytes.clear();
                cb.clear();
                //将读取的定位值 减去多读取的信息
                fc.position(fc.position() - sub);
                size = fc.read(bytes);
            }

            return stringBuffer;
        } catch (Exception e) {
            log.error("nio读取报错", e);
        }
        return null;
    }
}